﻿[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Items.LeafItem.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Items.LeafItem.debug.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.DataType.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.DataType.debug.js", "application/x-javascript")]

namespace BM.Tools.WebControls.MForm.Controls.Items
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Xml.Schema;
    using System.Xml.XPath;

    using AjaxControlToolkit;
    using BM.Tools.WebControls.MForm.Controls.Additions;
    using BM.Tools.WebControls.MForm.Controls.DefaultTemplates;
    using BM.Tools.WebControls.MForm.Controls.Helper;
    using BM.Tools.WebControls.MForm.Controls.ValueHandlers;        

    /// <summary>
    /// The repeatable offspring item that is created from the <see cref="Leaf" /> definition object
    /// </summary>
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Items.LeafItem.js")]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.DataType.js")]
    public class LeafItem : OffspringItem
    {
        public const string OptionalCssClass = "MForm-Op";

        public const string RequiredCssClass = "MForm-Rq";

        public const string InputCssClass = "MForm-In";

        public const string LeafCssClass = "MForm-Lf";

        private string defaultValue;

        private PlaceHolder labelPanel;

        private PlaceHolder afterValueHandlerHolder = new PlaceHolder();                

        /// <summary>
        /// Initializes a new instance of the <see cref="LeafItem"/> class.
        /// </summary>
        /// <param name="basis">
        /// The basis (the leaf object).
        /// </param>
        /// <param name="index">
        /// The index to disguise from other siblings
        /// </param>
        public LeafItem(Leaf basis, int index) : base(basis, index)
        {
            DefaultValue = basis.DefaultValue;
            ValueHandlerHolder = new ValueHandlerHolder(this);
            base.Controls.Add(ValueHandlerHolder);
        }

        /// <summary>
        /// Gets a place holder that will be displayed after the value handler.
        /// </summary>
        public PlaceHolder AfterValueHandlerHolder
        {
            get
            {
                return afterValueHandlerHolder;
            }
        }

        /// <summary>
        /// Gets the leaf object from which the item was created
        /// </summary>
        public new Leaf Basis
        {
            get
            {
                return base.Basis as Leaf;
            }
        }

        /// <summary>
        /// Gets Controls and if they are not initialized, creates them.
        /// </summary>
        public override ControlCollection Controls
        {
            get
            {
                EnsureChildControls();
                return base.Controls;
            }
        }

        /// <summary>
        /// Gets DataType of the value associated with the item.
        /// </summary>        
        public XmlTypeCode DataType
        {
            get
            {
                return Basis.DataType;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the item is optional.
        /// </summary>        
        public virtual bool IsOptional
        {
            get
            {
                if (IsOptionalInternal == null)
                {
                    return Basis.IsOptional;
                }

                return IsOptionalInternal.Value;
            }

            set
            {
                IsOptionalInternal = value;
            }
        }

        /// <summary>
        /// Gets or sets DefaultValue.
        /// </summary>
        public string DefaultValue
        {
            get
            {
                return defaultValue;
            }

            set
            {
                defaultValue = value;
            }
        }

        /// <summary>
        /// Gets Input.
        /// </summary>
        public WebControl Input
        {
            get
            {
                var controlHandler = ValueHandler as IWebControlValueHandler;
                if (controlHandler != null)
                {
                    return controlHandler.WebControl;
                }

                return null;
            }
        }

        /// <summary>
        /// Gets Label.
        /// </summary>
        public Label Label
        {
            get
            {
                EnsureInstantiateLabelTemplate();
                return LabelPanel.Controls.FirstOfType<Label>(true);
            }
        }

        /// <summary>
        /// Gets LeafType.
        /// </summary>
        public LeafType LeafType
        {
            get
            {
                return Basis.LeafType;
            }
        }

        /// <summary>
        /// Gets or sets Navigable to which the item is databound.
        /// </summary>
        public override IXPathNavigable Navigable
        {
            get
            {
                return base.Navigable;
            }

            set
            {
                base.Navigable = value;
                if (value != null)
                {
                    var navigator = value.CreateNavigator();
                    Value = navigator.Value;
                }
            }
        }

        /// <summary>
        /// Gets or sets the string representation of the value as it should appear in an xml document.
        /// </summary>
        public string Value
        {
            get
            {
                try
                {
                    return LeafItemValueFormatter.GetXmlValue(this);
                } 
                catch (FormatException e)
                {
                    return null;
                }
            }

            set
            {
                try
                {
                    LeafItemValueFormatter.SetXmlValue(this, value);
                }
                catch (FormatException e)
                {
                }
            }
        }

        /// <summary>
        /// Gets or sets the the string representation of the value conforming to the cultural context of the form.
        /// </summary>
        public string DataTypeValue
        {
            get
            {
                return ValueHandler.Value;
            }

            set
            {
                ValueHandler.Value = value;
            }
        }

        /// <summary>
        /// Gets or sets ValueHandler.
        /// </summary>
        public ValueHandler ValueHandler
        {
            get
            {
                return ValueHandlerHolder.ValueHandler;
            }

            set
            {
                ValueHandlerHolder.ValueHandler = value;
            }
        }

        internal bool? IsOptionalInternal
        {
            get
            {
                return ViewState.GetValue<bool?>("isOptional", null);
            }

            set
            {
                ViewState["isOptional"] = value.Value;
            }
        }

        /// <summary>
        /// Gets LabelPanel.
        /// </summary>
        /// <value>
        /// The label panel.
        /// </value>
        internal PlaceHolder LabelPanel
        {
            get
            {
                if (labelPanel == null)
                {
                    labelPanel = new PlaceHolder();
                    labelPanel.PreRender += EnsureInstantiateLabelTemplate;
                }
                
                return labelPanel;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the item should be validated.
        /// </summary>
        protected override bool HasReasonsNotToBeValidated
        {
            get
            {
                if (String.IsNullOrEmpty(Value) && IsOptional)
                {
                    return true;
                }

                return base.HasReasonsNotToBeValidated;
            }
        }

        /// <summary>
        /// Gets LabelTemplate.
        /// </summary>
        /// <value>
        /// The label template.
        /// </value>
        protected ITemplate LabelTemplate
        {
            get
            {
                return Basis.LabelHolder ?? new DefaultLabelTemplate(Basis);
            }
        }

        private ValueHandlerHolder ValueHandlerHolder
        {
            get;
            set;
        }

        /// <summary>
        /// Appends the associated xml to the specified parent node
        /// </summary>
        /// <param name="parent">
        /// The parent.
        /// </param>
        protected override void AppendXmlToParent(IXPathNavigable parent)
        {
            if (String.IsNullOrEmpty(Value))
            {
                if (Basis.IsNullable && LeafType == LeafType.Element)
                {
                    this.AppendNilElement(parent);
                }

                return;
            }

            switch (LeafType)
            {
                case LeafType.Element:
                    this.AppendElement(parent);
                    break;
                default:
                    this.AppendAttribute(parent);
                    break;
            }
        }

        /// <summary>
        /// custom child control creation
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            if (Basis.ShouldGenerateLabel)
            {
                Controls.AddAt(0, LabelPanel);
            }
            
            Controls.Add(AfterValueHandlerHolder);
            if (Basis.ValueHandlerFooterTemplate != null)
            {
                Basis.ValueHandlerFooterTemplate.InstantiateIn(AfterValueHandlerHolder);
            }

            Controls.Add(new RemovalButton(this));
        }

        /// <summary>
        /// returns the css classes that are ascribed to this item
        /// </summary>
        /// <returns>
        /// the css classes that are ascribed to this item
        /// </returns>
        protected override string[] InitalizationCssClasses()
        {
            var classes = new List<string>(base.InitalizationCssClasses()) { LeafCssClass };
            return classes.ToArray();
        }

        /// <summary>
        /// Adds the <see cref="DataTypeConstraint" /> constraint to the collection of constraints if there is none and if the ShouldAutomaticallyAddDataTypeConstraints property is set to true.
        /// </summary>
        /// <param name="e">
        /// The event args.
        /// </param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (Basis.Root.ShouldAutomaticallyAddDataTypeConstraints && !Additions.Constraints.OfType<DataTypeConstraint>().Any())
            {
                Additions.Add(new DataTypeConstraint());
            }
        }

        /// <summary>
        /// Adds custom css classes
        /// </summary>
        /// <param name="e">
        /// The event args
        /// </param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (IsOptional)
            {
                CssClasses.Remove(RequiredCssClass);
                CssClasses.Add(OptionalCssClass);
            } 
            else
            {
                CssClasses.Remove(OptionalCssClass);
                CssClasses.Add(RequiredCssClass);
            }
        }

        private void EnsureInstantiateLabelTemplate()
        {
            if (LabelPanel.Controls.Count == 0)
            {
                LabelTemplate.InstantiateIn(LabelPanel);
            }
        }

        private void EnsureInstantiateLabelTemplate(object sender, EventArgs e)
        {
            EnsureInstantiateLabelTemplate();
        }
    }
}